/*
 * Copyright (C) 2000-2008 Jens Nixdorf and various contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies of the Software, its documentation and marketing & publicity
 * materials, and acknowledgment shall be given in the documentation, materials
 * and software packages that this Software was used.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.mopore.jat.plugin;

import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mopore.jat.core.CorePlatformPlugin;
import org.mopore.jat.core.dashboard.Dashboard;
import org.mopore.jat.core.dashboard.DashboardInfo;
import org.mopore.jat.core.messaging.Message;
import org.mopore.jat.gui.IThemeChangeListener;
import org.mopore.jat.gui.SysTrayProxy;
import org.mopore.jat.gui.Theme;
import org.mopore.jat.gui.WindowController;
import org.mopore.jat.plugin.IJatPlugin.PluginLoadState;

public class PluginContextBaseImpl implements IPluginContextInternal {
	
	private static final String NOT_SUPORTED = "This can not be provided by plugin base. Plugin specidifc file access is availabel dircetly via plugin context agent wrapper.";

	private static final String LISTENER_ERROR_WILL_BE_REMOVED = "listener can not handle event and will be removed";

	private static final String PLUGIN_CAN_NOT_HANDLE_LEFT_CLICK = "plugin can not handle left click: ";

	private static final String PLUGIN_CAN_NOT_HANDLE_DOUBLE_CLICK = "plugin can not handle double click: ";
	
	private final Log log = LogFactory.getLog(getClass().getName());
	
	private final Map<String, IJatPluginInternal> plugins;

	private final SysTrayProxy sysTrayProxy;
	
	private final PluginEngine pluginEngine;
	
	private final Map<String, PluginLoadState> pluginLoadStatesMap;
	
	private final List<IThemeChangeListener> themeChangeListenerListenerList;
	
	private final Map<String, List<IMouseActionListener>> mouseActionListenerMap;
	
	private final Map<String, List<IPluginFocusListener>> pluginFocusListenerMap;
	
	private final List<IMouseActionListener> mouseActionListenerAllList;
		
	private final List<IPluginFocusListener> pluginFocusListenerAllList;
	
	private final Map<String, List<ITerminatedListener>> terminatedListenerMap;

	private final Dashboard dashboard;
	
	
	public PluginContextBaseImpl(
			PluginEngine pluginEngine,
			Map<String, IJatPluginInternal> plugins, 
			SysTrayProxy sysTrayProxy ) {
		this.plugins = plugins;
		this.sysTrayProxy = sysTrayProxy;
		this.pluginEngine = pluginEngine;
		this.pluginLoadStatesMap = new HashMap<String, PluginLoadState>();
		this.themeChangeListenerListenerList = new LinkedList<IThemeChangeListener>(); 
		this.mouseActionListenerMap = new HashMap<String, List<IMouseActionListener>>();
		this.pluginFocusListenerMap = new HashMap<String, List<IPluginFocusListener>>();
		this.mouseActionListenerAllList = new LinkedList<IMouseActionListener>();
		this.pluginFocusListenerAllList = new LinkedList<IPluginFocusListener>();
		this.terminatedListenerMap = new HashMap<String, List<ITerminatedListener>>();
		
		IPluginFileAccess fileAccess = 
			new PluginFileAccessJaxB( CorePlatformPlugin.class );
		WindowController windowController = pluginEngine.getWindowController();
		this.dashboard = new Dashboard( fileAccess, windowController );
		
		for (String key : plugins.keySet() ) {
			pluginLoadStatesMap.put( key,  PluginLoadState.NOTLOADED );
		}
	}

	
	
	
	public IJatPlugin getPlugin( String name ) {
		
		log.debug( "get plug-in with name: " + name );
		IJatPlugin plugin = plugins.get( name );
		return plugin;
	}


	public Map<String, IJatPluginInternal> getPlugInMap() {
		return plugins;
	}
	
	
	public PluginLoadState getPluginLoadState( String pluginName ) {
		return pluginLoadStatesMap.get( pluginName );
	}


	public void setPluginLoadState(String pluginName, PluginLoadState state) {
		pluginLoadStatesMap.put( pluginName, state );
	}



	public IJatPlugin getPluginWithFocus() {
		
		String pluginName = pluginEngine.getPluginWithFocus();
		if ( pluginName == null ) {
			return null;
		}
		IJatPlugin plugin = plugins.get( pluginName );
		return plugin;
	}


	public void requestFocusChange( String pluginName ) {
		pluginEngine.changeFocus( pluginName );
		
	}


	public void requestGlobalShutDown() {
		pluginEngine.shutdownEngine();
	}

	
	
	public void addThemeChangeListener( IThemeChangeListener themeChangeListener ) {
		
		log.debug( "IThemeChangeListener added" );
		themeChangeListenerListenerList.add( themeChangeListener );
	}



	public void doubleClicked( IJatPlugin clickedPlugin ) throws PluginException {
		String focusedName = pluginEngine.getPluginWithFocus();
		IJatPluginInternal plugin = plugins.get( focusedName );

		String name = null;
		try {
			name = plugin.getMetaInformation().getPluginName();
			plugin.doubleClicked( plugin );
			
		} catch (PluginException e) {
			String message = PLUGIN_CAN_NOT_HANDLE_DOUBLE_CLICK + name;
			log.error( message, e );
			pluginEngine.terminatePlugin( focusedName, message );
		}
		
		List<IMouseActionListener> list = mouseActionListenerMap.get( focusedName );
		if ( list != null ) {
			Iterator<IMouseActionListener> iterator = list.iterator();
			while (iterator.hasNext()) {
				IMouseActionListener listener = iterator.next();
				try {
					listener.doubleClicked( plugin );
				}
				catch (Exception e) {
					log.error( LISTENER_ERROR_WILL_BE_REMOVED, e );
					iterator.remove();
				}
			}
		}
		Iterator<IMouseActionListener> allIterator = 
			mouseActionListenerAllList.iterator();
		while (allIterator.hasNext()) {
			IMouseActionListener listener = allIterator.next();
			try {
				listener.doubleClicked( plugin );
			}
			catch (Exception e) {
				log.error( LISTENER_ERROR_WILL_BE_REMOVED, e );
				allIterator.remove();
			}
		}		
	}


	public void leftClicked( IJatPlugin clickedPlugin ) throws PluginException {
		String focusedName = pluginEngine.getPluginWithFocus();
		IJatPluginInternal plugin = plugins.get( focusedName );

		String name = null;
		try {
			name = plugin.getMetaInformation().getPluginName();
			plugin.leftClicked(plugin);
			
		} catch (PluginException e) {
			String message = PLUGIN_CAN_NOT_HANDLE_LEFT_CLICK + name;
			log.error( message, e );
			pluginEngine.terminatePlugin( focusedName, message );
		}
		
		List<IMouseActionListener> list = mouseActionListenerMap.get( focusedName );
		if ( list != null ) {
			Iterator<IMouseActionListener> iterator = list.iterator();
			while (iterator.hasNext()) {
				IMouseActionListener listener = iterator.next();
				try {
					listener.leftClicked( plugin );
				}
				catch (Exception e) {
					log.error( PLUGIN_CAN_NOT_HANDLE_LEFT_CLICK, e );
					iterator.remove();
				}
			}
		}
		Iterator<IMouseActionListener> allIterator = 
			mouseActionListenerAllList.iterator();
		while (allIterator.hasNext()) {
			IMouseActionListener listener = allIterator.next();
			try {
				listener.leftClicked( plugin );
			}
			catch (Exception e) {
				log.error( LISTENER_ERROR_WILL_BE_REMOVED, e );
				allIterator.remove();
			}
		}
	}



	public void onThemeChanged( Theme newTheme ) {
		
		Iterator<IThemeChangeListener> iterator = 
			themeChangeListenerListenerList.iterator();
		while (iterator.hasNext()) {
			IThemeChangeListener listener = iterator.next();
			try {
				listener.onThemeChanged(newTheme);
			}
			catch (Exception e) {
				log.error( PLUGIN_CAN_NOT_HANDLE_LEFT_CLICK, e );
				iterator.remove();
			}
		}
	}



	public void onFocusGained(IJatPlugin plugin) throws PluginException {

		String name = null;
			name = plugin.getMetaInformation().getPluginName();
		IJatPluginInternal internalPlugin = plugins.get( name );
		internalPlugin.onFocusGained( plugin );
		
		List<IPluginFocusListener> list = pluginFocusListenerMap.get( name );
		if ( list != null ) {
			Iterator<IPluginFocusListener> iterator = list.iterator();
			while (iterator.hasNext()) {
				IPluginFocusListener listener = iterator.next();
				try {
					listener.onFocusGained( plugin );
				}
				catch (Exception e) {
					log.error( LISTENER_ERROR_WILL_BE_REMOVED, e );
					iterator.remove();
				}
			}
		}
		
		Iterator<IPluginFocusListener> allIterator = 
			pluginFocusListenerAllList.iterator();
		while (allIterator.hasNext()) {
			IPluginFocusListener listener = allIterator.next();
			try {
				listener.onFocusGained( plugin );
			}
			catch (Exception e) {
				log.error( LISTENER_ERROR_WILL_BE_REMOVED, e );
				allIterator.remove();
			}
		}
		
	}


	public void onFocusLost(IJatPlugin plugin) throws PluginException {

		String name = null;
			name = plugin.getMetaInformation().getPluginName();
		IJatPluginInternal internalPlugin = plugins.get( name );
		internalPlugin.onFocusLost( plugin );
		
		List<IPluginFocusListener> list = pluginFocusListenerMap.get( name );
		if ( list != null ) {
			Iterator<IPluginFocusListener> iterator = list.iterator();
			while (iterator.hasNext()) {
				IPluginFocusListener listener = iterator.next();
				try {
					listener.onFocusLost(plugin);
				}
				catch (Exception e) {
					log.error( LISTENER_ERROR_WILL_BE_REMOVED, e );
					iterator.remove();
				}
			}
		}
		Iterator<IPluginFocusListener> allIterator = 
			pluginFocusListenerAllList.iterator();
		while (allIterator.hasNext()) {
			IPluginFocusListener listener = allIterator.next();
			try {
				listener.onFocusLost( plugin );
			}
			catch (Exception e) {
				log.error( LISTENER_ERROR_WILL_BE_REMOVED, e );
				allIterator.remove();
			}
		}		
	}


	public void addMouseActionListener(IMouseActionListener mouseActionListener, String... pluginNames ) {
		
		if ( pluginNames.length == 0 ) {
			mouseActionListenerAllList.add( mouseActionListener );
		}
		else {
			for (String pluginName : pluginNames) {
				List<IMouseActionListener> list = 
					mouseActionListenerMap.get( pluginName );
				if ( list == null ) {
					list = new LinkedList<IMouseActionListener>();
					mouseActionListenerMap.put( pluginName, list );
				}
				
				list.add( mouseActionListener );
			}
		}
	}
	
	
	public void addPluginFocusListener( IPluginFocusListener pluginFocusListener, String... pluginNames ) {
		
		if ( pluginNames.length == 0 ) {
			pluginFocusListenerAllList.add( pluginFocusListener );
		}
		else {
			for (String pluginName : pluginNames) {
				List<IPluginFocusListener> list = 
					pluginFocusListenerMap.get( pluginName );
				if ( list == null ) {
					list = new LinkedList<IPluginFocusListener>();
					pluginFocusListenerMap.put( pluginName, list );
				}
				
				list.add( pluginFocusListener );
			}
		}
	}


	public String getPluginNameWithFocus() {
		
		String pluginName = pluginEngine.getPluginWithFocus();
		return pluginName;
	}


	

	public void onTerminated(String pluginName) {
		
		List<ITerminatedListener> list = terminatedListenerMap.get( pluginName );
		if ( list != null ) {
			Iterator<ITerminatedListener> iterator = list.iterator();
			while (iterator.hasNext()) {
				ITerminatedListener listener = iterator.next();
				try {
					listener.onTerminated( pluginName );
				}
				catch (Exception e) {
					log.error( LISTENER_ERROR_WILL_BE_REMOVED, e );
					iterator.remove();
				}
			}
		}
	}


	public void addTerminatedListener(String pluginName, ITerminatedListener terminatedListener) {
		
		List<ITerminatedListener> list = 
			terminatedListenerMap.get( pluginName );
		if ( list == null ) {
			list = new LinkedList<ITerminatedListener>();
			terminatedListenerMap.put( pluginName, list );
		}
		
		list.add( terminatedListener );
	}


	public IPluginFileAccess getFileAccess() {
		throw new UnsupportedOperationException( NOT_SUPORTED );
	}


	@Override
	public Theme getCurrentTheme() {
		return pluginEngine.getCurrentTheme();
	}


	@Override
	public void updateTrayIcon(String text ) {
		sysTrayProxy.updateAppearance( text );
	}

	public List<IJatPluginInternal> getFocusablePlugins() {

		List<IJatPluginInternal> pluginList = new LinkedList<IJatPluginInternal>();

		Collection<IJatPluginInternal> values = plugins.values();

		for (IJatPluginInternal plugin : values) {
			boolean isFucasable = plugin.isFocusable();
			if (isFucasable) {
				pluginList.add(plugin);
			}
		}
		return pluginList;
	}


	/**
	 * This method has been overridden.
	 * @param message
	 * @see org.mopore.jat.plugin.IPluginContext#showMessage(org.mopore.jat.core.messaging.Message)
	 */
	@Override
	public void showMessage(Message message) {
		sysTrayProxy.showMessage( message );
	}



	/**
	 * This method has been overridden.
	 * @param message
	 * @param messageAction
	 * @see org.mopore.jat.plugin.IPluginContext#showMessage(org.mopore.jat.core.messaging.Message, java.awt.event.ActionListener)
	 */
	@Override
	public void showMessage(Message message, ActionListener messageAction) {
		sysTrayProxy.showMessage( message, messageAction );
	}




	/**
	 * This method has been overridden.
	 * @return
	 * @see org.mopore.jat.plugin.IPluginContext#hasFocus()
	 */
	@Override
	public boolean hasFocus() {
		throw new UnsupportedOperationException( "can not be called directly" );
	}


	/**
	 * This method has been overridden.
	 * @return
	 * @see org.mopore.jat.plugin.IPluginContext#getWindowController()
	 */
	@Override
	public IWindowController getWindowController() {
		WindowController windowController = pluginEngine.getWindowController();
		return windowController;
	}




	/**
	 * This method has been overridden.
	 * @see org.mopore.jat.plugin.IPluginContextInternal#showDashboard()
	 */
	@Override
	public void showDashboard() {
		Theme theme = getCurrentTheme();
		dashboard.show( theme );
	}



	/**
	 * This method has been overridden.
	 * @param dashboardInfo
	 * @param pluginName
	 * @see org.mopore.jat.plugin.IPluginContextInternal#pinDashboardInfo(org.mopore.jat.core.dashboard.DashboardInfo, java.lang.String)
	 */
	@Override
	public void pinDashboardInfo(DashboardInfo dashboardInfo, String pinnerId) {
		dashboard.pin( dashboardInfo, pinnerId );
	}



	/**
	 * This method has been overridden.
	 * @param title
	 * @param text
	 * @param validThru
	 * @see org.mopore.jat.plugin.IPluginContext#pinToDashboard(java.lang.String, java.lang.String, java.util.Calendar)
	 */
	@Override
	public void pinToDashboard(String title, String text, Calendar validThru) {
		
		long id = System.currentTimeMillis();
		DashboardInfo dashboardInfo = new DashboardInfo();
		dashboardInfo.setId( id );
		dashboardInfo.setTitle( title );
		dashboardInfo.setText( text );
		dashboardInfo.setValidTill( validThru );
		
		dashboard.pin( dashboardInfo, CorePlatformPlugin.class.getName() );
		
	}
	
	
}
